/**
 * @author LIU YUAN
 * @date FEB 24, 2013 11:12:21 AM
 * @param <T>
 */
#include "stdafx.h"
#include <Shlwapi.h>
#include "cmd.h"
#include "asm.h"
#include "AddEax.h"
#include "ShortJmp.h"
#include "NearJmp.h"
#include "Push.h"
#include "PushPtr.h"
#include "PushEax.h"
#include "Call.h"
#include "CallEax.h"
#include "Ret.h"
#include "DataBlock.h"

using namespace upupc;

extern HWND cmdHwnd;
extern HWND uiHwnd;
extern HMODULE module;

HWND CreateCmdWindow(HINSTANCE hInstance){
	WNDCLASSEX wc;
	//
    // register class of CMD window.
    //
    wc.cbSize         = sizeof(WNDCLASSEX);
    wc.style          = CS_INPUTSTAR;
    wc.lpfnWndProc    = CMDWndProc;
    wc.cbClsExtra     = 0;
    wc.cbWndExtra     = 2 * sizeof(LONG);
    wc.hInstance      = hInstance;
    wc.hCursor        = LoadCursor( NULL, IDC_ARROW );
    wc.hIcon          = NULL;
    wc.lpszMenuName   = (LPTSTR)NULL;
    wc.lpszClassName  = CLSNAME_CMD;
    wc.hbrBackground  = NULL;
    wc.hIconSm        = NULL;
	
    if( !RegisterClassEx( (LPWNDCLASSEX)&wc ) )
        return NULL;
	DWORD pid = GetCurrentProcessId();
	TCHAR pidStr[20];
	_stprintf_s(pidStr,20,TEXT("LY%d"),pid);	
	return CreateWindow(CLSNAME_CMD,pidStr,WS_DISABLED,0,0,0,0,NULL,(HMENU)0,hInstance,NULL);
}

BOOL WINAPI DestroyCmdWindow(HINSTANCE hInstance){
	DestroyWindow(cmdHwnd);
	UnregisterClass(CLSNAME_CMD,hInstance);
	return TRUE;
}

LRESULT WINAPI CMDWndProc(HWND hWnd,UINT message,WPARAM wParam,	LPARAM lParam){
	LONG lRet = 0L;
	switch(message)
    {
	case WM_CREATE:
		SetWindowLongPtr(hWnd,LYGWL_PID,GetCurrentProcessId());
		SetWindowLongPtr(hWnd,LYGWL_MODULE,(LONG)module);
		SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_NOACTIVATE|SWP_NOZORDER);
		break;
	case WM_DESTROY:
		break;
	case WM_COPYDATA:
		ServiceProc(hWnd,message,wParam,lParam);
		break;
	default:
		return DefWindowProc(hWnd,message,wParam,lParam);
    }
	return lRet;
}

LRESULT WINAPI ServiceProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam){
	LONG lRet = 0L;
	if(message!=WM_COPYDATA){
		return lRet;
	}

	PCOPYDATASTRUCT copyData = (PCOPYDATASTRUCT)lParam;
	if(!copyData){
		return lRet;
	}

	switch(wParam){
	case CMD_SEND_STRING:
		SendString(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_SEND_STRING_IME:
		SendStringIme(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_FREE_SELF:
		FreeSelf(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_FREE_IME:
		FreeIme(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_LOAD_DLL:
		LoadDll(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_FREE_DLL:
		FreeDll(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_INVOKE_DLL_METHOD:
		InvokeDllMethod(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_GET_MODULE_HANDLE:
		ReturnModuleHandle(hWnd,copyData->lpData,copyData->cbData);
		break;
	case CMD_GET_MODULE_NAME:
		ReturnModuleName(hWnd,copyData->lpData,copyData->cbData);
		break;
	}
	return lRet;
}

void ReturnModuleHandle(HWND hWnd,PVOID lpData,DWORD cbData){
	LONG lRet = 0L;
	PGetData gd = (PGetData)lpData;
	HANDLE hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,gd->cbData,gd->lpName);
	if (hMapFile == NULL){
		return;
	}
	LPVOID pBuf = MapViewOfFile(hMapFile,FILE_MAP_ALL_ACCESS,0,0,gd->cbData);
	PDWORD h = (PDWORD)pBuf;

	if(cbData==sizeof(GetData)){
		*h = (DWORD)module;
	}else{
		LPSTR moduleName = (LPSTR)((DWORD)gd + sizeof(GetData));
		HMODULE hModule = GetModuleHandleA(moduleName);
		*h = (DWORD)hModule;
	}
	UnmapViewOfFile(pBuf);
    CloseHandle(hMapFile);
	return;
}

void ReturnModuleName(HWND hWnd,PVOID lpData,DWORD cbData){
	LONG lRet = 0L;
	PGetData gd = (PGetData)lpData;
	HANDLE hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,gd->cbData,gd->lpName);
	if (hMapFile == NULL){
		return;
	}
	LPVOID pBuf = MapViewOfFile(hMapFile,FILE_MAP_ALL_ACCESS,0,0,gd->cbData);
	if (pBuf == NULL){
		CloseHandle(hMapFile);
		return;
	}

	if(cbData==sizeof(GetData)){
		GetModuleFileNameA(NULL,(LPSTR)pBuf,gd->cbData);
	}else{
		HMODULE hModule = (HMODULE)(gd + sizeof(GetData));
		GetModuleFileNameA(hModule,(LPSTR)pBuf,gd->cbData);
	}

	UnmapViewOfFile(pBuf);
    CloseHandle(hMapFile);
	return;
}

void SendString(HWND hWnd,PVOID lpData,DWORD cbData){
	HKL curHkl = GetKeyboardLayout(NULL);
	char name[MAX_PATH];
	ImmGetIMEFileNameA(curHkl,name,MAX_PATH);
	MessageBoxA(NULL,name,"",0);
}
void SendStringIme(HWND hWnd,PVOID lpData,DWORD cbData){
	HKL hkl = CInjectUtils::GetImeByName(IME_NAMEA);
	if(!hkl){
		return;
	}
	HKL curHkl = GetKeyboardLayout(NULL);
	if(curHkl!=hkl){
		HWND mainHwnd = CInjectUtils::getSingletonWindow(GetCurrentProcessId(),0,0,1);
		CInjectUtils::ActiveWindow(mainHwnd);
		ActivateKeyboardLayout(hkl,KLF_SETFORPROCESS);
	}
	
	HIMC hIMC = (HIMC)GetWindowLong(uiHwnd,IMMGWL_IMC);
	if (!hIMC){
		return;
	}

	LPINPUTCONTEXT lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
	if(lpIMC){
		LPCOMPOSITIONSTRING lpCompStr=(LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
		LPWSTR pText = (LPWSTR)lpData;
		LPWSTR pResult = (LPWSTR)((LPBYTE)(lpCompStr) + (lpCompStr)->dwResultStrOffset);
		ZeroMemory(pResult,lpCompStr->dwResultStrLen*sizeof(WCHAR));
		CopyMemory(pResult,pText,cbData);
		
		lpCompStr->dwResultStrLen=wcslen(pResult);
		lpCompStr->dwCompStrLen=0;
		ImmUnlockIMCC(lpIMC->hCompStr);

		PKEYINFO ki = GETKEYINFO(lpIMC);
		ki->uKey = VK_HOME;
		ki->isProcess = TRUE;

		PostMessage(lpIMC->hWnd, WM_KEYDOWN,VK_PROCESSKEY,0x01470001);

		ImmUnlockIMC(hIMC);
	}
}

DWORD WINAPI FreeLibraryThreadProc(_In_  LPVOID lpParameter){
	Sleep(200);
	FreeLibraryAndExitThread(module,0);
	return 0;
}

void FreeSelf(HWND hWnd,PVOID lpData,DWORD cbData){
	DestroyCmdWindow(module);
	CreateThread(NULL, 0, FreeLibraryThreadProc, 0, 0, NULL); 
}

void FreeIme(HWND hWnd,PVOID lpData,DWORD cbData){
	HKL hkl = *((HKL*)lpData);
	if(!hkl){
		return;
	}

	HKL hkls[20];
	GetKeyboardLayoutList(20,hkls);
	BOOL isHklOk = FALSE;
	for(int i=0;i<20;i++){
		if(hkl==hkls[i]){
			isHklOk = TRUE;
			break;
		}
	}

	if(!isHklOk){
		return;
	}

	HKL curHkl = GetKeyboardLayout(NULL);
	if(hkl==curHkl){
		ActivateKeyboardLayout((HKL)HKL_NEXT,KLF_SETFORPROCESS);
	}

	HMODULE hModule = GetModuleHandleW(L"imm32.dll");
	PnfImmFreeLayout pImmFreeLayout = (PnfImmFreeLayout)GetProcAddress(hModule,"ImmFreeLayout");
	if(!pImmFreeLayout){
		return;
	}
	DestroyCmdWindow((HINSTANCE)module);

	Asm mAsm;
	Push pushHkl;
	Call callImmFreeLayout;
	Ret ret;

	pushHkl.setParameter((DWORD)hkl);
	callImmFreeLayout.setParameter((DWORD)pImmFreeLayout);
	ret.setParameter(4);

	mAsm.append(&pushHkl);
	mAsm.append(&callImmFreeLayout);
	mAsm.append(&ret);

	DWORD size = MAX_PATH;
	LPVOID threadAddr = VirtualAlloc(NULL, size, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);

	CHAR* codes = mAsm.parse((DWORD)threadAddr);

	CopyMemory(threadAddr,codes,mAsm.size());

	CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadAddr, 0, 0, NULL); 
}

void LoadDll(HWND hWnd,PVOID lpData,DWORD cbData){
	PDllInfo di = (PDllInfo)lpData;

	LPSTR pModName = PathFindFileNameA(di->dllname);
	HMODULE hModule = GetModuleHandleA(pModName);
	if(!hModule){
		hModule = LoadLibraryA(di->dllname);
	}
	
	FARPROC proc = GetProcAddress(hModule,di->method);
	if(proc){
		proc();
	}

	PGetData gd = (PGetData)((DWORD)lpData + sizeof(DllInfo));
	HANDLE hMapFile = CreateFileMappingA(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,gd->cbData,gd->lpName);
	if (hMapFile == NULL){
		return;
	}
	LPVOID pBuf = MapViewOfFile(hMapFile,FILE_MAP_ALL_ACCESS,0,0,gd->cbData);
	if (pBuf == NULL){
		CloseHandle(hMapFile);
		return;
	}
	PDWORD h = (PDWORD)pBuf;
	*h = (DWORD)hModule;
	
	UnmapViewOfFile(pBuf);
    CloseHandle(hMapFile);
}
void FreeDll(HWND hWnd,PVOID lpData,DWORD cbData){
	PDllInfo idf = (PDllInfo)lpData;
	HMODULE hModule = GetModuleHandleA(idf->dllname);
	if(hModule!=module){
		while (FreeLibrary(hModule));
	}
}

void InvokeDllMethod(HWND hWnd,PVOID lpData,DWORD cbData){
	PDllInfo idf = (PDllInfo)lpData;
	
	HMODULE hModule = GetModuleHandleA(idf->dllname);
	FARPROC proc = GetProcAddress(hModule,idf->method);
	if(!proc){
		return;
	}
	if(!idf->cbParamNum){
		proc();
		return;
	}
	DWORD num = idf->cbParamNum;
	PDWORD paramsAddr = (PDWORD)((DWORD)idf + sizeof(DllInfo));

	_asm{
		PUSHAD
	start:
		mov  ebx,num
		Test ebx,ebx
		jz   callproc
		mov  eax,num
		sub  eax,1
		mov  dx,4
		mul  dx
		add  eax,[paramsAddr]
		push [eax];
		dec  num
		jmp  start
	callproc:
		call proc;
		POPAD
	}
}